#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include "acdaemon.h"

struct __AcDaemon {
	int umask;
	char* work_dir;
	char* fstdin;
	char* fstdout;
	char* fstderr;
};

typedef struct __AcDaemon _AcDaemon;

static void _do_fork();
static void _close_fds();
static void _reopen_std_fds(const char*, const char*, const char*);




AcDaemon acdaemon_new()
{
	AcDaemon d = calloc(1, sizeof(_AcDaemon));
	if(!d) {
		return NULL;
	}

	d->umask = 0;
	d->work_dir = strdup("/");
	d->fstdin = strdup("/dev/null");
	d->fstdout = strdup("/dev/null");
	d->fstderr = strdup("/dev/null");
}

void acdaemon_free(AcDaemon acd)
{
	free(acd->work_dir);

	free(acd->fstdin);
	free(acd->fstdout);
	free(acd->fstderr);

	free(acd);
}

int acdaemon_get_umask(AcDaemon acd)
{
	return acd->umask;
}

AcDaemon acdaemon_set_umask(AcDaemon acd, int mask)
{
	acd->umask = mask;
	return acd;
}

/*int acdaemon_get_max_fd(AcDaemon acd)
{
	return acd->max_fd;
}

AcDaemon acdaemon_set_max_fd(AcDaemon acd, int fd)
{
	acd->max_fd = fd;
	return acd;
}*/

char* acdaemon_get_work_dir(AcDaemon acd)
{
	return acd->work_dir;
}

AcDaemon acdaemon_set_work_dir(AcDaemon acd, const char *work_dir)
{
	free(acd->work_dir);
	acd->work_dir = strdup(work_dir);
	return acd;
}

char* acdaemon_get_stdin(AcDaemon acd)
{
	return acd->fstdin;
}

AcDaemon acdaemon_set_stdin(AcDaemon acd, const char *f)
{
	free(acd->fstdin);
	acd->fstdin = strdup(f);
	return acd;
}

char* acdaemon_get_stdout(AcDaemon acd)
{
	return acd->fstdout;
}

AcDaemon acdaemon_set_stdout(AcDaemon acd, const char *f)
{
	free(acd->fstdout);
	acd->fstdout = strdup(f);
	return acd;
}

char* acdaemon_get_stderr(AcDaemon acd)
{
	return acd->fstderr;
}

AcDaemon acdaemon_set_stderr(AcDaemon acd, const char *f)
{
	free(acd->fstderr);
	acd->fstderr = strdup(f);
	return acd;
}

int acdaemon_daemonize_default()
{
	AcDaemon acd = acdaemon_new();
	return acdaemon_daemonize(acd);
}

int acdaemon_daemonize(AcDaemon acd)
{
	if(!acd) {
		return 0;
	}

	_do_fork();
	setsid();
	_do_fork();

	chdir(acd->work_dir);
	umask(acd->umask);

	_close_fds();
	_reopen_std_fds(acd->fstdin, acd->fstdout, acd->fstderr);

	return 1;
}

/*privates*/
void _do_fork()
{
	int pid = fork();
	if(pid < 0) {
		exit(EXIT_FAILURE);
	} else if(pid > 0) {
		//this is parent
		_exit(EXIT_SUCCESS);
	}
}


void _close_fds()
{
	close(0);
	close(1);
	close(2);
}

void _reopen_std_fds(const char* fin, const char *fout, const char *ferr)
{
	freopen(fin, "r", stdin);
	freopen(fout, "w", stdout);
	freopen(ferr, "w", stderr);
}
